﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Globalization;
using System.IO;
using System.Net;
using System.Net.Configuration;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Threading;
using DM4.InfoClasses;
using DM4Script;
using sscore;
using sscore.Utils;
using System.Data;
using System.ServiceModel.Web;
using System.Data.Services;
using System.Data.Services.Common;
using System.Linq;

namespace DM4
{
    public partial class Entities
    {

        public static string connectionLog;
    }

    public class LogDataservice : DataService<Entities>
    {
        public static void InitializeService(DataServiceConfiguration config)
        {
            config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
            config.SetServiceOperationAccessRule("*", ServiceOperationRights.All);
            config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
        }
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    [AspNetCompatibilityRequirements(RequirementsMode =
        AspNetCompatibilityRequirementsMode.Allowed)]

    public partial class DM4 : IDM4, IDisposable, IDM4Core
    {
        static readonly object Locker = new object();
        static readonly object SessionLocker = new object();

        private readonly Log _sysLog;
        private readonly Params _prm;
        private readonly Params _ver;

        private SqlConnection _pconn;

        public SqlConnection _conn
        {
            get
            {
                if (_pconn.State != ConnectionState.Open)
                    _pconn.Open();

                return _pconn;
            }

            set
            {
                _pconn = value;
            }
        }

        private ServerStatus _status;
        public ServerStatus Status
        {
            get {return _status; }
            set { _status = value; }
        }

        private string _sysDaemonsCache;
        private string _sysModulesCache;

        private bool _inClient;

        private readonly bool _debug;

        private readonly Dictionary<string, DaemonEntry> _daemons = new Dictionary<string, DaemonEntry>();
        private readonly Dictionary<string, ModuleEntry> _modules = new Dictionary<string, ModuleEntry>();

        private readonly Dictionary<string, SessionEntry> _sessions = new Dictionary<string, SessionEntry>();
        private readonly Dictionary<string, ScriptParse> _runningScripts = new Dictionary<string, ScriptParse>();

        #region writeBackLog отложеная запись лога пока не поднят демон лога
        private Stack<object> writeBackLog = new Stack<object>();
        #endregion

        public SqlConnection GetDBConnection()
        {
            return _conn;
        }

        public GlobalInfo GetInfo()
        {
            BeginFunction("GetInfo");
            GlobalInfo gi = null;
            try
            {
                gi = new GlobalInfo
                {
                    Version = String.Format("{0}.{1}", _ver.GetStringParam("MajorVersion"), _ver.GetStringParam("MinorVersion")),
                    ServerTime = DateTime.Now,
                    StartupTime = DateTime.Now - _prm.GetDateTimeParam("StartTime"),
                    Traffic = (double)SysUtils.GetTraffic() / 1024 / 1024
                };

            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, MessageType.Error);
            }
            EndFunction("GetInfo");
            return gi;
        }

        public string Version()
        {
            return String.Format("{0}.{1}", _ver.GetStringParam("MajorVersion"),
                                 _ver.GetStringParam("MinorVersion"));
        }

        public XMLInfo Login(string user, string password)
        {
            BeginFunction("Login");

            if (Status == ServerStatus.Loading)
                return new XMLInfo(new OperationResult(DM4Consts.ErrorServerLoading, "Server is loading..."));
            if (Status == ServerStatus.Shutdown)
                return new XMLInfo(new OperationResult(DM4Consts.ErrorServerShutdown, "Server is shutdown...")); 
            
            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
            Thread.CurrentThread.CurrentCulture.NumberFormat.CurrencyDecimalSeparator = ".";
            Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-US");

            WriteLog(String.Format("Login. User: [{0}]", user));

            SetUsrSessionId(user, password, Guid.NewGuid().ToString(), DateTime.Now.ToString(CultureInfo.InvariantCulture));

            string sql = String.Format("select [usr_id], [usr_login], [usr_password], [usr_email], [usr_fullname] , [usr_access_level], [usr_session_id], [usr_last_access] from users where usr_login = '{0}' AND usr_password = '{1}'", user.Trim(), password.Trim());
            WriteDebugLog(sql);

            XMLInfo xi = new XMLInfo(new OperationResult(DM4Consts.Ok));

            SqlCommand sel = new SqlCommand(sql, _conn);
            using (SqlDataReader reader = sel.ExecuteReader())
            {
                try
                {
                    WriteLog(reader.HasRows ? String.Format("Login for User: [{0}] successfully", user) :
                                                         String.Format("Login for User: [{0}] failed", user), MessageType.Info);

                    xi.Load(reader, "users");

                }
                catch (Exception ex)
                {
                    WriteLog(ex.Message, MessageType.Error);
                }
            }

            EndFunction("Login");
            return xi;
        }

        private bool SetUsrSessionId(string user, string password, string usrSessionId, string usrlastAccess)
        {
            BeginFunction("SetUsrSessionId");

            bool result = true;

            string update = String.Format("UPDATE [users] SET [usr_session_id] = @usrSessionId, [usr_last_access] = @usrlastAccess WHERE [usr_login] = @user AND [usr_password] = @password");

            try
            {
                using (SqlCommand command = new SqlCommand(update, _conn))
                {
                    command.Parameters.AddWithValue("@usrSessionId", usrSessionId);
                    command.Parameters.AddWithValue("@usrlastAccess", usrlastAccess);
                    command.Parameters.AddWithValue("@user", user);
                    command.Parameters.AddWithValue("@password", password);
                    command.ExecuteNonQuery();
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, MessageType.Error);
                result = false;
            }

            EndFunction("SetUsrSessionId");

            return result;
        }

        private OperationResult SetUsrSessionId(string usrSessionId)
        {
            BeginFunction("SetUsrSessionId");

            OperationResult result = OperationResult.ErrorSessionId(usrSessionId);

            string update = String.Format("UPDATE [users] SET [usr_session_id] = @New_usrSessionId, [usr_last_access] = @usrlastAccess WHERE [usr_session_id] = @usrSessionId");

            try
            {
                using (SqlCommand command = new SqlCommand(update, _conn))
                {
                    string guid = Guid.NewGuid().ToString();
                    string access = DateTime.Now.ToString(CultureInfo.InvariantCulture);

                    command.Parameters.AddWithValue("@usrSessionId", usrSessionId);
                    command.Parameters.AddWithValue("@New_usrSessionId", guid);
                    command.Parameters.AddWithValue("@usrlastAccess", access);
                    if (command.ExecuteNonQuery() == 1) result.SetValue(DM4Consts.Ok, access, guid);
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, MessageType.Error);
                result.Message = ex.Message;
            }

            EndFunction("SetUsrSessionId");

            return result;
        }

        private OperationResult GetUserIdBySession(string usrSessionId)
        {
            BeginFunction("GetUserIdBySession");

            OperationResult result = new OperationResult(DM4Consts.DefaultNULLId);

            if (Status == ServerStatus.Loading)
                return new OperationResult(DM4Consts.ErrorServerLoading, "Server is loading...");
            if (Status == ServerStatus.Shutdown)
                return new OperationResult(DM4Consts.ErrorServerShutdown, "Server is shutdown...");
            
            //OperationResult resultNew = new OperationResult(DM4Consts.Ok);
            //resultNew.Result = 1;
            //resultNew.LastAccess = "111";
            //resultNew.UsrSessionId = "222";
            //return resultNew;

            const string select = "SELECT [usr_id] FROM [users] WHERE [usr_session_id] = @usrSessionId";

            try
            {
                using (SqlCommand command = new SqlCommand(select, _conn))
                {
                    command.Parameters.AddWithValue("@usrSessionId", usrSessionId);
                    result.Result = (int)command.ExecuteScalar();

                    OperationResult session = SetUsrSessionId(usrSessionId);
                    if (!session) return session;

                    result.LastAccess = session.LastAccess;
                    result.UsrSessionId = session.UsrSessionId;
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message);
                WriteLog(String.Format("User SessionId: {0} not found.", usrSessionId));

                result = OperationResult.ErrorSessionId(usrSessionId);
            }

            EndFunction("GetUserIdBySession");

            return result;
        }

        public void Shutdown()
        {
            Status = ServerStatus.Shutdown;

            WriteLog("Shutdown DM4 server...");

            WriteLog("Shutdown DM4 server complete");
        }

        public void Dispose()
        {

        }

        public DM4(Log sysLog, Params prm, SqlConnection conn, bool InClient = false)
        {
            Status = ServerStatus.Loading;

            new HttpWebRequestElement { UseUnsafeHeaderParsing = true };
            ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
            ServicePointManager.DefaultConnectionLimit = 1000;

            _inClient = InClient;

            _sysLog = sysLog;
            _prm = prm;
            _conn = conn;

            _debug = _prm.GetBooleanParam("Debug");
            WriteLog(String.Format("Debug Mode = {0}", _debug));

            WriteLog("Start daemons...");
            LoadDaemons("", false);
            WriteLog(String.Format("Start daemons complete, found : {0}", _daemons.Count));

            WriteLog("Configure module groups...");

            ConfigGroup();
            WriteLog("Configure module groups operation complete");

            ResumeScripts();

            string verFileName = String.Format("{0}\\{1}", prm.GetStringParam("WorkDir"), "version.xml");
            _ver = Params.CreateNonCryptedParamsWithoutEtalon(verFileName);

            //XMLInfo xi = GetScriptList(1);
            //AddScript(1, "test2", "description2", "text2");
            //EditScript(2, 3, "test2", "description", "text");
            //DeleteScript(1, 1);
            //DeleteScript(1, 3);
            //WriteLog(xi.XML);

            Status = ServerStatus.Runs;
        }

        private void AddModule(Interfaces type, FileInfo originalFile, FileInfo cachedFile, int grpId = 0, bool startDaemonInDebug = true, string workDir = "")
        {
            BeginFunction("AddModule");
            IModuleBasicInterface mi = GetInterface(cachedFile.FullName, type);

            if (mi != null)
            {
                switch (type)
                {
                    case Interfaces.Module:
                        ModuleEntry me = new ModuleEntry
                            {
                                Code = mi.GetInfo(DmConsts.ModuleCode.Trim().ToUpper()),
                                Description = mi.GetInfo(DmConsts.Description.ToUpper()),
                                Module = (IModule)mi,
                                Help = mi.GetHelp(),
                                Version = mi.GetInfo(DmConsts.Version.ToUpper()),
                                ShortFileName = originalFile.Name,
                                FileName = originalFile.FullName,
                                GrpId = grpId,
                                WorkDir = workDir
                            };

                        if (_modules.ContainsKey(me.Code))
                        {
                            _modules[me.Code].FileName = me.FileName;
                        }
                        else
                        {
                            _modules.Add(me.Code.ToUpper(), me);
                            WriteLog(String.Format("Module with code [{0}] added to cache. File [{1}]", me.Code, me.ShortFileName));
                        }
                        break;

                    case Interfaces.Daemon:
                        DaemonEntry de = new DaemonEntry
                        {
                            Code = mi.GetInfo(DmConsts.ModuleCode.Trim().ToUpper()),
                            Module = (IDaemon)mi,
                            Version = mi.GetInfo(DmConsts.Version.Trim().ToUpper()),
                            Help = mi.GetHelp(),
                            ShortFileName = originalFile.Name,
                            FileName = originalFile.FullName
                        };

                        if (_daemons.ContainsKey(de.Code.ToUpper()))
                        {
                            WriteLog(String.Format("Daemon with code [{0}] allready exists! Error in file [{1}]", de.Code, de.ShortFileName), MessageType.Error);
                        }
                        else
                        {
                            _daemons.Add(de.Code.ToUpper(), de);
                            WriteLog(String.Format("Daemon with code [{0}] added to cache. File [{1}]", de.Code, de.ShortFileName));

                            if ((!_daemons[de.Code.ToUpper()].Module.IsDebug()) || (startDaemonInDebug))
                            {
                                _daemons[de.Code.ToUpper()].Module.Init(this);
                                _daemons[de.Code.ToUpper()].Module.Start();
                            }
                        }
                        break;
                }
            }
            else
            {
                WriteLog(String.Format("File [{0}] is not a DM4 module", originalFile.FullName), MessageType.Error);
            }
            EndFunction("AddModule");
        }

        #region сортировка, демон лога загрузится самый первый
        public class DMN_Comparer : IComparer<FileInfo>
        {
            Dictionary<string, int> SortName = new Dictionary<string, int>() { { LogConsts.ModuleCode.ToUpper(), 1 }, { MailSenderConsts.ModuleCode.ToUpper(), 2 } };
            public int Compare(FileInfo x, FileInfo y)
            {
                int xSort = SortName.ContainsKey(Path.GetFileNameWithoutExtension(x.Name).ToUpper()) ? SortName[Path.GetFileNameWithoutExtension(x.Name).ToUpper()] : 1000;
                int ySort = SortName.ContainsKey(Path.GetFileNameWithoutExtension(y.Name).ToUpper()) ? SortName[Path.GetFileNameWithoutExtension(y.Name).ToUpper()] : 1000;
                return xSort.CompareTo(ySort);
            }
        }
        #endregion

        private void LoadDaemons(string moduleCode = "", bool startDaemonInDebug = true)
        {
            BeginFunction("LoadDaemons");
            _sysDaemonsCache = GenerateCacheDir(CacheType.Daemon);

            DirectoryInfo directory = new DirectoryInfo(_sysDaemonsCache);
            if (!directory.Exists) directory.Create();

            directory = new DirectoryInfo(_prm.GetStringParam("DaemonsDir"));
            if (!directory.Exists) directory.Create();

            //load all daemons            
            if (String.IsNullOrEmpty(moduleCode))
            {
                WriteLog("Scanning daemons directory...");
                //copy all daemons to cache
                FileInfo[] files = directory.GetFiles(DmConsts.ModulesExtension);
                WriteLog(String.Format("Found {0} files", files.Length));

                _daemons.Clear();
                Array.Sort(files, new DMN_Comparer());
                Array.ForEach(files, file =>
                                         {
                                             string newfilename = String.Format(@"{0}\\{1}", _sysDaemonsCache, file.Name);
                                             file.CopyTo(newfilename, true);
                                             AddModule(Interfaces.Daemon, file, new FileInfo(newfilename), 0, startDaemonInDebug);
                                         });
            }
            else
            { //reload one daemon
                if (_daemons.ContainsKey(moduleCode.Trim().ToUpper()))
                {
                    string filename = _daemons[moduleCode.Trim().ToUpper()].FileName;
                    string newfilename = String.Format(@"{0}\\{1}", _sysDaemonsCache, _daemons[moduleCode.Trim().ToUpper()].ShortFileName);
                    File.Copy(filename, newfilename, true);
                    _daemons.Remove(moduleCode.Trim().ToUpper());
                    AddModule(Interfaces.Daemon, new FileInfo(filename), new FileInfo(newfilename), 0, startDaemonInDebug);
                }
            }
            EndFunction("LoadDaemons");
        }

        private string GenerateCacheDir(CacheType cachetype)
        {
            BeginFunction("GenerateCacheDir");
            string dir;
            switch (cachetype)
            {
                case CacheType.Daemon:
                    dir = String.Format("{0}\\cache\\daemons\\{1}", _prm.GetStringParam("WorkDir"), Guid.NewGuid());
                    break;
                default:
                    dir = String.Format("{0}\\cache\\modules\\{1}", _prm.GetStringParam("WorkDir"), Guid.NewGuid());
                    break;
            }

            if (!String.IsNullOrEmpty(dir))
            {
                DirectoryInfo directory = new DirectoryInfo(dir);
                if (!directory.Exists) directory.Create();
                WriteLog(String.Format("Generate cache dir name : [{0}]", dir));
            }

            EndFunction("GenerateCacheDir");
            return dir;
        }

        private void LoadModules(string homedir, int grpId)
        {
            BeginFunction("LoadModules");
            DirectoryInfo directory = new DirectoryInfo(_sysModulesCache);
            if (!directory.Exists) directory.Create();

            directory = new DirectoryInfo(homedir);
            if (!directory.Exists) directory.Create();

            WriteLog("Scanning modules directory ...");
            //copy all daemons to cache
            FileInfo[] files = directory.GetFiles(DmConsts.ModulesExtension);
            WriteLog(String.Format("Found {0} files", files.Length));

            Array.ForEach(files, file =>
                                     {
                                         string newfilename = String.Format(@"{0}\\{1}", _sysModulesCache, file.Name);
                                         file.CopyTo(newfilename, true);
                                         AddModule(Interfaces.Module, file, new FileInfo(newfilename), grpId, true, homedir);
                                     });
            EndFunction("LoadModules");
        }

        private IModuleBasicInterface GetInterface(string filename, Interfaces interfaceType)
        {
            BeginFunction("GetInterface");
            IModuleBasicInterface moduleIntrface = null;

            string interfaceName;
            switch (interfaceType)
            {
                case Interfaces.Daemon:
                    interfaceName = "DM4.IDaemon";
                    break;
                default:
                    interfaceName = "DM4.IModule";
                    break;
            }

            Assembly assembly = Assembly.LoadFile(filename);

            try
            {
                foreach (var type in assembly.GetTypes())
                {
                    Type iface = type.GetInterface(interfaceName);
                    if (iface != null)
                    {
                        moduleIntrface = (IModuleBasicInterface)Activator.CreateInstance(type);
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message);
                WriteLog(String.Format("File {0} has oldest interface {1}. Recompile module, please.", filename, interfaceName));
            }

            EndFunction("GetInterface");
            return moduleIntrface;
        }

        #region functions for working with Daemons

        public OperationResult StopDaemon(string usrSessionId, string code = "")
        {
            BeginFunction("StopDaemon");

            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);
            if (!userIdOpResult) return userIdOpResult;

            OperationResult or = new OperationResult(userIdOpResult, DM4Consts.Ok);

            if (String.IsNullOrEmpty(code))
            {
                //stop all daemons
                foreach (DaemonEntry de in _daemons.Values)
                {
                    WriteLog(String.Format("Attempt to stop daemon [{0}]", de.Code));
                    OperationResult or1 = de.Module.Stop();
                    WriteLog(String.Format("Daemon [{0}]. Stop result : {1}", de.Code, or1.Message));
                }
            }
            else
            {
                if (_daemons.ContainsKey(code.Trim().ToUpper()))
                {
                    if (_daemons[code.Trim().ToUpper()].Module.GetStatus() == DaemonStatus.Run)
                    {
                        WriteLog(String.Format("Attempt to stop daemon [{0}]", _daemons[code.Trim().ToUpper()].Code));
                        OperationResult or1 = _daemons[code.Trim().ToUpper()].Module.Stop();
                        WriteLog(String.Format("Daemon [{0}]. Stop result : {1}", code.Trim().ToUpper(), or1.Message));
                    }
                    else
                    {
                        WriteLog(String.Format("Daemon [{0}] is not in Run status. The stop are impossible", code.Trim().ToUpper()));
                        or.Result = DM4Consts.DaemonInvalidStatus;
                        or.Message = String.Format("Daemon [{0}] is not in Run status. The stop are impossible", code.Trim().ToUpper());
                    }
                }
            }

            EndFunction("StopDaemon");
            return or;
        }

        public OperationResult StartDaemon(string usrSessionId, string code = "", bool startDaemonInDebug = true)
        {
            BeginFunction("StartDaemon");

            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);
            if (!userIdOpResult) return userIdOpResult;

            OperationResult or = new OperationResult(userIdOpResult, DM4Consts.Ok);

            if (String.IsNullOrEmpty(code))
            {
                //start all daemons
                LoadDaemons("", startDaemonInDebug);
            }
            else
            {
                if (_daemons.ContainsKey(code.Trim().ToUpper()))
                {
                    WriteLog(String.Format("Attempt to start daemon [{0}]", code.Trim().ToUpper()));

                    if (_daemons[code.Trim().ToUpper()].Module.GetStatus() == DaemonStatus.Stop)
                    {
                        LoadDaemons(code.Trim().ToUpper(), startDaemonInDebug);
                        WriteLog(String.Format("Daemon [{0}] started", code.Trim().ToUpper()));
                    }
                    else
                    {
                        WriteLog(String.Format("Daemon [{0}] is not in Stop status. The start are impossible", code.Trim().ToUpper()));
                        or.Result = DM4Consts.DaemonInvalidStatus;
                        or.Message = String.Format("Daemon [{0}] is not in Stop status. The start are impossible", code.Trim().ToUpper());
                    }
                }
                else
                {
                    WriteLog(String.Format("Daemon [{0}] already started", code.Trim().ToUpper()));
                    or.Result = DM4Consts.ErrorUnknownDaemon;
                    or.Message = String.Format("Daemon with code [{0}] not found", code.Trim().ToUpper());
                }
            }

            EndFunction("StartDaemon");
            return or;
        }

        public OperationResult RestartDaemon(string usrSessionId, string code)
        {
            BeginFunction("RestartDaemon");

            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);
            if (!userIdOpResult) return userIdOpResult;

            OperationResult or = new OperationResult(userIdOpResult, DM4Consts.Ok);

            if (or.Result == DM4Consts.Ok)
                or = StartDaemon(code);

            EndFunction("RestartDaemon");
            return or;
        }

        public OperationResult ExecuteDaemonCommand(string usrSessionId, string code, string command, object parameters)
        {
            BeginFunction("ExecuteDaemonCommand");

            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);
            if (!userIdOpResult) return userIdOpResult;

            OperationResult or = ExecuteDaemonCommand(code, command, parameters);
            or.UsrSessionId = userIdOpResult.UsrSessionId;

            EndFunction("ExecuteDaemonCommand");
            return or;
        }

        private OperationResult ExecuteDaemonCommand(string code, string command, object parameters)
        {
            OperationResult or = new OperationResult(DM4Consts.Ok);

            if (_daemons.ContainsKey(code.ToUpper()))
            {
                DaemonEntry de = _daemons[code.ToUpper()];
                if (de.Module.GetStatus() == DaemonStatus.Run)
                {
                    #region writeBackLog отложеная запись лога пока не поднят демон лога
                    if (writeBackLog != null && code.ToUpper() == LogConsts.ModuleCode.ToUpper() && command == LogCommands.InsertLog)
                    {
                        foreach (var param in writeBackLog)
                            or = de.Module.Execute(LogCommands.InsertLog, param);
                        writeBackLog = null;
                    }
                    #endregion

                    or = de.Module.Execute(command.ToUpper(), parameters);
                }
                else if (code.ToUpper() == LogConsts.ModuleCode.ToUpper() && command == LogCommands.InsertLog)
                {
                    writeBackLog.Push(parameters);
                }
                else
                {
                    or.Result = DM4Consts.DaemonInvalidStatus;
                    or.Message = String.Format("Daemon [{0}] is stopped. Execute command dont possible", de.Code);
                }
            }
            else
            {
                or.Result = DM4Consts.ErrorUnknownDaemon;
                or.Message = String.Format("Daemon with code [{0}] not found", code.Trim().ToUpper());
            }

            return or;
        }

        public DaemonStatus GetDaemonStatus(string code)
        {
            return _daemons.ContainsKey(code.Trim().ToUpper()) ?
                _daemons[code.Trim().ToUpper()].Module.GetStatus() :
                DaemonStatus.UnknownDaemon;
        }

        public OperationResult GetDaemonsList(string usrSessionId)
        {
            BeginFunction("GetDaemonsList");

            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);
            if (!userIdOpResult) return userIdOpResult;

            OperationResult or = new OperationResult(userIdOpResult, DM4Consts.Ok);
            if (_daemons.Count > 0)
            {
                Dictionary<string, ParamEntry> daemons = new Dictionary<string, ParamEntry>();
                foreach (var value in _daemons) daemons.Add(value.Value.Code, new ParamEntry(value.Key));
                or.Message = StringUtils.ParamsToString(daemons);
            }

            EndFunction("GetDaemonsList");
            return or;
        }

        #endregion

        #region functions for working with Groups

        private void SetGroupStatus(int id, bool enabled)
        {
            BeginFunction("SetGroupStatus");
            string sql = String.Format("update groups set grp_disabled = '{0}' where grp_id = {1}", SysUtils.ConvertToDBBool(!enabled), id);
            //WriteDebugLog(sql);

            using (SqlCommand upd = new SqlCommand(sql, _conn))
                upd.ExecuteNonQuery();

            EndFunction("SetGroupStatus");
        }

        private void ConfigGroup(int id = -1)
        {
            BeginFunction("ConfigGroup");
            _sysModulesCache = GenerateCacheDir(CacheType.Module);

            string sql = id == -1 ? "select * from groups" : String.Format("select * from groups where grp_id = {0}", id);
            WriteDebugLog(sql);

            using (SqlCommand sc = new SqlCommand(sql, _conn))
            {
                using (SqlDataReader reader = sc.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        string homedir = SysUtils.ConvertFromDBVal<string>(reader["grp_homedir"]);
                        string groupName = SysUtils.ConvertFromDBVal<string>(reader["grp_name"]);
                        int grpId = SysUtils.ConvertFromDBVal<int>(reader["grp_id"]);

                        if (grpId == DM4Consts.DefaultModulesGroupId) //not have home dir = default group
                        {
                            WriteLog(String.Format("Group [{0}] configure as default", groupName));
                            homedir = _prm.GetStringParam("ModulesDir");
                        }
                        else
                        {
                            homedir = String.Format("{0}\\{1}", _prm.GetStringParam("WorkDir"), groupName);
                        }

                        sql = String.Format("UPDATE groups set grp_homedir = '{0}' WHERE grp_id = 1", homedir);
                        WriteDebugLog(sql);


                        using (SqlCommand upd = new SqlCommand(sql, _conn))
                            upd.ExecuteNonQuery();

                        if (Directory.Exists(homedir))
                        {
                            //WriteLog("homedir=" + homedir);                            
                            SetGroupStatus(grpId, true);
                            LoadModules(homedir, grpId);
                        }
                        else
                        {
                            SetGroupStatus(grpId, false);
                        }
                    }

                }
            }
            EndFunction("ConfigGroup");
        }

        private GroupInfo GetGroupInfo(string name)
        {
            return GetGroupInfo(-1, name);
        }

        private GroupInfo GetGroupInfo(int id, string name = "")
        {
            BeginFunction("GetGroupInfo");

            GroupInfo gi = new GroupInfo();

            string sql = name == "" ? String.Format("select * from groups left outer join scripts on grp_script = scr_id where grp_id = {0}", id) :
                String.Format("select * from groups left outer join scripts on grp_script = scr_id where LTRIM(RTRIM(UPPER(grp_name))) = '{0}'", name.Trim().ToUpper());

            WriteDebugLog(sql);

            SqlCommand sel = new SqlCommand(sql, _conn);
            using (SqlDataReader reader = sel.ExecuteReader())
            {
                if (reader.HasRows)
                    try
                    {
                        reader.Read();

                        gi.GrpId = SysUtils.ConvertFromDBVal<int>(reader["grp_id"]);
                        gi.GrpName = SysUtils.ConvertFromDBVal<string>(reader["grp_name"]);
                        gi.GrpScriptId = SysUtils.ConvertFromDBVal<int>(reader["grp_script"]);
                        gi.GrpScriptName = SysUtils.ConvertFromDBVal<string>(reader["scr_name"]);
                        gi.GrpDescription = SysUtils.ConvertFromDBVal<string>(reader["grp_description"]);
                        gi.GrpHomedir = SysUtils.ConvertFromDBVal<string>(reader["grp_homedir"]);
                        gi.GrpCreationDate = SysUtils.ConvertFromDBVal<DateTime>(reader["grp_creation_date"]);
                        gi.GrpUsr = SysUtils.ConvertFromDBVal<int>(reader["grp_usr"]);
                        gi.GrpDisabled = SysUtils.ConvertFromDBBool(SysUtils.ConvertFromDBVal<string>(reader["grp_disabled"]));
                    }
                    catch (Exception ex)
                    {
                        WriteLog(String.Format("Error occured while GetGroupInfo: Id [{0}], name [{1}] {2}", id, name, ex.Message), MessageType.Error);
                    }
            }

            EndFunction("GetGroupInfo");
            return gi;
        }
        #endregion functions for working with Groups

        #region functions for working with Scripts

        private ScriptInfo GetScriptInfo(string name)
        {
            return GetScriptInfo(DM4Consts.DefaultNULLId, name);
        }

        private ScriptInfo GetScriptInfo(int id, string name = "")
        {
            BeginFunction("GetScriptInfo");

            ScriptInfo si = new ScriptInfo();

            string sql = name == "" ? String.Format("select scripts.*, u1.usr_fullname as scr_last_modified_name, u2.usr_fullname as scr_author_name  from scripts left outer join users u1 on scr_modified_by = u1.usr_id left outer join users u2 on scr_author = u2.usr_id where scr_id = {0}", id) :
                String.Format("select scripts.*, u1.usr_fullname as scr_last_modified_name, u2.usr_fullname as scr_author_name  from scripts left outer join users u1 on scr_modified_by = u1.usr_id left outer join users u2 on scr_author = u2.usr_id where LTRIM(RTRIM(UPPER(scr_name))) = '{0}'", name.Trim().ToUpper());

            WriteDebugLog(sql);

            SqlCommand sel = new SqlCommand(sql, _conn);
            using (SqlDataReader reader = sel.ExecuteReader())
            {
                if (reader.HasRows)
                    try
                    {
                        reader.Read();
                        si.ScrId = SysUtils.ConvertFromDBVal<int>(reader["scr_id"]);
                        si.ScrName = SysUtils.ConvertFromDBVal<string>(reader["scr_name"]);
                        si.ScrDescription = SysUtils.ConvertFromDBVal<string>(reader["scr_description"]);
                        si.ScrText = SysUtils.ConvertFromDBVal<string>(reader["scr_text"]);
                        si.ScrModifiedByName = SysUtils.ConvertFromDBVal<string>(reader["scr_last_modified_name"]);
                        si.ScrModifiedBy = SysUtils.ConvertFromDBVal<int>(reader["scr_modified_by"]);
                        si.ScrLastModified = SysUtils.ConvertFromDBVal<DateTime>(reader["scr_last_modified"]);
                        si.ScrVersion = SysUtils.ConvertFromDBVal<int>(reader["scr_version"]);
                        si.ScrAuthorName = SysUtils.ConvertFromDBVal<string>(reader["scr_author_name"]);
                        si.ScrAuthor = SysUtils.ConvertFromDBVal<int>(reader["scr_author"]);
                        si.ScrCreated = SysUtils.ConvertFromDBVal<DateTime>(reader["scr_created"]);
                    }

                    catch (Exception ex)
                    {
                        WriteLog(String.Format("Error occured while GetScriptInfo: Id [{0}], name [{1}] {2}", id, name, ex.Message), MessageType.Error);
                    }
            }

            EndFunction("GetScriptInfo");
            return si;
        }
        #endregion functions for working with Scripts

        #region functions for working with Tasks and Sessions

        private void StartOneTask(object prm)
        {
            DmTaskParams tPrm = (DmTaskParams)prm;
            CancellationToken ct = tPrm.Ct;

            if (!ct.IsCancellationRequested)
            {
                OperationResult or = tPrm.Module.Init(this, tPrm);
                if (or.Result == DM4Consts.Ok)
                    tPrm.Module.Start();
            }
        }

        private void PrepareSession(Prototypes.ScriptTask task, Prototypes.Log.Point session, Dictionary<string, ParamEntry> prm, Dictionary<string, Dictionary<string, ParamEntry>> parentParams, string modFile, string sessionId = "", bool needStart = true, Int64 totalWorkTime = 0, Int64 sessionTraffic = 0, Int64 sessionRecords = 0, Int64 sessionErrors = 0)
        {
            foreach (KeyValuePair<string, ParamEntry> key in task.SubParams)
            {
                if (prm.ContainsKey(key.Key.ToUpper()))
                    prm.Remove(key.Key.ToUpper());

                prm.Add(key.Key.ToUpper(), key.Value);
            }

            if (prm.ContainsKey(DM4Consts.StartPosition.ToUpper()))
            {
                session.X = Convert.ToInt64(prm[DM4Consts.StartPosition.ToUpper()].GetValue());
                prm.Remove(DM4Consts.StartPosition.ToUpper());
            }

            prm.Add(DM4Consts.StartPosition.ToUpper(), new ParamEntry(session.X.ToString(), ParamType.Integer));

            if (prm.ContainsKey(DM4Consts.EndPositon.ToUpper()))
            {
                session.Y = Convert.ToInt64(prm[DM4Consts.EndPositon.ToUpper()].GetValue());
                prm.Remove(DM4Consts.EndPositon.ToUpper());
            }

            prm.Add(DM4Consts.EndPositon.ToUpper(), new ParamEntry(session.Y.ToString(), ParamType.Integer));

            lock (SessionLocker)
            {
                StartSession(task.TskSessionId, modFile, task.UsrId, session.X, session.Y, prm, parentParams, sessionId, needStart, totalWorkTime, sessionTraffic, sessionRecords, sessionErrors);
            }
        }

        private OperationResult DoTask(Prototypes.ScriptTask task, Dictionary<string, Dictionary<string, ParamEntry>> parentParams)
        {
            BeginFunction("DoTask");
            OperationResult or = new OperationResult();

            try
            {
                //search module
                if (ModuleExists((string)task.SubParams[DM4Consts.ModuleCode.ToUpper()].GetValue()))
                {
                    string modFile = GetModuleFileNameByCode((string)task.SubParams[DM4Consts.ModuleCode.ToUpper()].GetValue());
                    string shortModFile = GetModuleFileNameByCode((string)task.SubParams[DM4Consts.ModuleCode.ToUpper()].GetValue(), true);
                    //copy file to cache
                    string cacheDir = GenerateCacheDir(CacheType.Module);
                    File.Copy(modFile, String.Format("{0}\\{1}", cacheDir, shortModFile));

                    //search task sessions in database
                    string sql = String.Format("SELECT * from sessions WHERE UPPER(sess_task) = '{0}'", task.TskSessionId.ToUpper());
                    WriteDebugLog(sql);

                    using (SqlCommand sc = new SqlCommand(sql, _conn))
                    {
                        IModule moduleIntrface = (IModule)GetInterface(modFile, Interfaces.Module);
                        if (moduleIntrface != null)
                        {
                            using (SqlDataReader reader = sc.ExecuteReader())
                            {
                                if (reader.HasRows) //resume old sessions
                                {
                                    while (reader.Read())
                                    {
                                        Dictionary<string, ParamEntry> prm = new Dictionary<string, ParamEntry>();

                                        Int64 x = SysUtils.ConvertFromDBVal<Int64>(reader["sess_startpos"]);
                                        Int64 y = SysUtils.ConvertFromDBVal<Int64>(reader["sess_endpos"]);
                                        string sessionId = SysUtils.ConvertFromDBVal<string>(reader["sess_id"]);
                                        string sessionStatus = SysUtils.ConvertFromDBVal<string>(reader["sess_status"]);
                                        Int64 sessionTotalTime = SysUtils.ConvertFromDBVal<Int64>(reader["sess_total_time"]);
                                        Int64 sessionTraffic = SysUtils.ConvertFromDBVal<Int64>(reader["sess_traffic"]);
                                        Int64 sessionRecords = SysUtils.ConvertFromDBVal<Int64>(reader[CnSessions.Records]);
                                        Int64 sessionErrors = SysUtils.ConvertFromDBVal<Int64>(reader[CnSessions.Errors]);

                                        Prototypes.Log.Point session = new Prototypes.Log.Point(x, y);
                                        PrepareSession(task, session, prm, parentParams, modFile, sessionId, sessionStatus == DM4Consts.SessionRun, sessionTotalTime, sessionTraffic, sessionRecords, sessionErrors);
                                    }
                                }
                                else //start new sessions
                                {

                                    List<Prototypes.Log.Point> sessions = moduleIntrface.GetSessions(Convert.ToInt32(task.Params[ScriptConst.SessionsCount.ToUpper()].GetValue()));

                                    foreach (Prototypes.Log.Point session in sessions)
                                    {
                                        Dictionary<string, ParamEntry> prm = new Dictionary<string, ParamEntry>();
                                        PrepareSession(task, session, prm, parentParams, modFile);
                                    }
                                }
                            }
                        }
                    }
                }
                else
                {
                    or.Result = DM4Consts.ErrorModuleNotExists;
                    or.Message = String.Format("Module with code [{0}] not found", task.SubParams[DM4Consts.ModuleCode.ToUpper()].GetValue());
                }
            }
            catch (Exception ex)
            {
                or.Result = DM4Consts.ErrorUnknown;
                or.Message = ex.Message;
            }

            EndFunction("DoTask");
            return new OperationResult();
        }

        private void UpdateSession(string sessionId, Int64 records, Int64 errors, double progress, string status, bool updateStatus, bool updateRecords, bool updateProgress, bool updateErrors, Int64 traffic, Int64 workTime, int resultCode = DM4Consts.SessionResultCodeNull, string resultMessage = DM4Consts.SessionResultMessageNull)
        {
            BeginFunction("UpdateSession");
            string sql = String.Format("SELECT * from sessions WHERE UPPER(sess_id) = '{0}'", sessionId.ToUpper());
            WriteDebugLog(sql);

            using (SqlCommand sc = new SqlCommand(sql, _conn))
            {
                using (SqlDataReader reader = sc.ExecuteReader())
                {
                    if (reader.HasRows) //resume old sessions
                    {
                        while (reader.Read())
                        {
                            Int64 sessionRecords = SysUtils.ConvertFromDBVal<Int64>(reader[CnSessions.Records]);
                            double sessionProgress;
                            string sessionStatus;

                            Int64 sessionErrors = SysUtils.ConvertFromDBVal<Int64>(reader[CnSessions.Errors]);

                            string sessionTask = SysUtils.ConvertFromDBVal<string>(reader[CnSessions.Task]);

                            if (updateErrors)
                                sessionErrors += errors;

                            if (updateProgress)
                                sessionProgress = progress;
                            else
                                sessionProgress = SysUtils.ConvertFromDBVal<double>(reader[CnSessions.Progress]);

                            if (updateRecords)
                                sessionRecords += records;

                            if (updateStatus)
                                sessionStatus = status;
                            else
                                sessionStatus = SysUtils.ConvertFromDBVal<string>(reader[CnSessions.Status]);

                            if (resultCode == DM4Consts.SessionResultCodeNull) resultCode = SysUtils.ConvertFromDBVal<int>(reader[CnSessions.ResultCode]);
                            if (resultMessage == DM4Consts.SessionResultMessageNull) resultMessage = SysUtils.ConvertFromDBVal<string>(reader[CnSessions.ResultMessage]);

                            string ssessionProgress = sessionProgress.ToString("0.00", CultureInfo.InvariantCulture);

                            sql = String.Format(@"UPDATE [sessions] 
                                                    SET [sess_errors] = @sess_errors, 
                                                        [sess_progress] = @sess_progress, 
                                                        [sess_records] = @sess_records, 
                                                        [sess_status] = @sess_status, 
                                                        [sess_traffic] = @sess_traffic, 
                                                        [sess_total_time] = @sess_total_time, 
                                                        {0}    
                                                        [sess_result_code] = @sess_result_code,
                                                        [sess_result_message] = @sess_result_message
                                                    WHERE UPPER([sess_id]) = @sess_id", sessionStatus == DM4Consts.SessionComplete ? "[sess_end_time] = @sess_end_time," : "");

                            WriteDebugLog(sql);


                            using (SqlCommand upd = new SqlCommand(sql, _conn))
                            {
                                upd.Parameters.AddWithValue("@sess_errors", sessionErrors);
                                upd.Parameters.AddWithValue("@sess_progress", ssessionProgress);
                                upd.Parameters.AddWithValue("@sess_records", sessionRecords);
                                upd.Parameters.AddWithValue("@sess_status", sessionStatus);
                                upd.Parameters.AddWithValue("@sess_traffic", traffic);
                                upd.Parameters.AddWithValue("@sess_total_time", workTime);

                                if (sessionStatus == DM4Consts.SessionComplete) upd.Parameters.AddWithValue("@sess_end_time", DateTime.Now);

                                upd.Parameters.AddWithValue("@sess_result_code", resultCode);
                                upd.Parameters.AddWithValue("@sess_result_message", resultMessage ?? "");

                                upd.Parameters.AddWithValue("@sess_id", sessionId);

                                upd.ExecuteNonQuery();
                            }


                            //check complete session
                            if (status == DM4Consts.SessionComplete || status == DM4Consts.SessionWait)
                            {//check task complete
                                sql = String.Format(@"SELECT [sessions].*, [rsh_sessions_id], [tsk_name], [tsk_param] 
                                                        FROM [sessions], [tasks], [run_script_header] 
                                                        WHERE [sess_task] = [tsk_session_id] AND [tsk_rsh] = [rsh_id] AND UPPER([sess_task]) = '{0}'", sessionTask.ToUpper());

                                WriteDebugLog(sql);

                                int completeSessions = 0;
                                int waitSessions = 0;
                                int RunSessions = 0;
                                int sessionsCount = 0;
                                string scriptId = "";
                                string taskName = "";
                                int sessionResultCode = DM4Consts.SessionResultCodeNull;
                                Dictionary<string, ParamEntry> tskParam = new Dictionary<string, ParamEntry>();


                                using (SqlCommand sc1 = new SqlCommand(sql, _conn))
                                {
                                    using (SqlDataReader reader2 = sc1.ExecuteReader())
                                    {
                                        if (reader2.HasRows) //resume old sessions
                                        {
                                            Dictionary<string, ParamEntry> retParameters = new Dictionary<string, ParamEntry>(); // результирующие параметры таска

                                            while (reader2.Read())
                                            {
                                                sessionsCount++;
                                                sessionStatus = SysUtils.ConvertFromDBVal<string>(reader2[CnSessions.Status]);
                                                sessionResultCode = SysUtils.ConvertFromDBVal<int>(reader2[CnSessions.ResultCode]);
                                                tskParam = StringUtils.StringToParams(SysUtils.ConvertFromDBVal<string>(reader2[CnTask.Param]));

                                                #region игнорирование ошибок параметр:IgnoreErrors
                                                if (!tskParam.ContainsKey(ScriptConst.IgnoreErrors) || !tskParam[ScriptConst.IgnoreErrors])
                                                    if (sessionResultCode != DM4Consts.SessionResultCodeNull && sessionResultCode != DM4Consts.SessionResultCodeOk && sessionResultCode != DM4Consts.SessionResultTerminateByUser)
                                                    {
                                                        AbortTask(sessionTask.ToUpper());//abort all sessions
                                                        break;
                                                    }
                                                #endregion

                                                if (sessionStatus == DM4Consts.SessionRun)
                                                {
                                                    RunSessions++;
                                                    break;
                                                }
                                                else if (sessionStatus == DM4Consts.SessionComplete)
                                                    completeSessions++;
                                                else if (sessionStatus == DM4Consts.SessionWait)
                                                    waitSessions++;

                                                scriptId = SysUtils.ConvertFromDBVal<string>(reader2["rsh_sessions_id"]);
                                                taskName = SysUtils.ConvertFromDBVal<string>(reader2["tsk_name"]);

                                                #region формирование параметров
                                                if (waitSessions == 0)
                                                {
                                                    if (retParameters.ContainsKey(ScriptConst.ResulRecords))
                                                        retParameters[ScriptConst.ResulRecords] += new ParamEntry(SysUtils.ConvertFromDBVal<long>(reader2[CnSessions.Records]));
                                                    else
                                                        retParameters.Add(ScriptConst.ResulRecords, new ParamEntry(SysUtils.ConvertFromDBVal<long>(reader2[CnSessions.Records])));

                                                    if (retParameters.ContainsKey(ScriptConst.ResulErrors))
                                                        retParameters[ScriptConst.ResulErrors] += new ParamEntry(SysUtils.ConvertFromDBVal<long>(reader2[CnSessions.Errors]));
                                                    else
                                                        retParameters.Add(ScriptConst.ResulErrors, new ParamEntry(SysUtils.ConvertFromDBVal<long>(reader2[CnSessions.Errors])));

                                                    if (retParameters.ContainsKey(ScriptConst.ResulTraffic))
                                                        retParameters[ScriptConst.ResulTraffic] += new ParamEntry(SysUtils.ConvertFromDBVal<long>(reader2[CnSessions.Traffic]));
                                                    else
                                                        retParameters.Add(ScriptConst.ResulTraffic, new ParamEntry(SysUtils.ConvertFromDBVal<long>(reader2[CnSessions.Traffic])));

                                                    if (retParameters.ContainsKey(ScriptConst.ResulStartTime))
                                                    {
                                                        if (retParameters[ScriptConst.ResulStartTime] > SysUtils.ConvertFromDBVal<DateTime>(reader2[CnSessions.StartTime]))
                                                            retParameters[ScriptConst.ResulStartTime] = new ParamEntry(SysUtils.ConvertFromDBVal<DateTime>(reader2[CnSessions.StartTime]));
                                                    }
                                                    else
                                                        retParameters.Add(ScriptConst.ResulStartTime, new ParamEntry(SysUtils.ConvertFromDBVal<DateTime>(reader2[CnSessions.StartTime])));

                                                    if (retParameters.ContainsKey(ScriptConst.ResulEndTime))
                                                    {
                                                        if (retParameters[ScriptConst.ResulEndTime] > SysUtils.ConvertFromDBVal<DateTime>(reader2[CnSessions.EndTime]))
                                                            retParameters[ScriptConst.ResulEndTime] = new ParamEntry(SysUtils.ConvertFromDBVal<DateTime>(reader2[CnSessions.EndTime]));
                                                    }
                                                    else
                                                        retParameters.Add(ScriptConst.ResulEndTime, new ParamEntry(SysUtils.ConvertFromDBVal<DateTime>(reader2[CnSessions.EndTime])));
                                                }
                                                #endregion
                                            }

                                            if (sessionsCount == completeSessions && _runningScripts.ContainsKey(scriptId)) //task complete
                                                RunTaskOn(_runningScripts[scriptId], taskName, retParameters);

                                            long tskId, rshId;
                                            if (GetRshIdAndTskIdFromSession(sessionId, out rshId, out  tskId))
                                            {
                                                if (sessionsCount == waitSessions + completeSessions)
                                                    UpdateTasks(tskId, CnTask.Status, TskStatus.Wait);
                                                
                                                if(RunSessions > 0)
                                                    UpdateTasks(tskId, CnTask.Status, TskStatus.Run);

                                                UpdateScriptStatus(rshId);
                                            }
                                        }
                                    }
                                }

                            }
                        }
                    }
                }
            }

            EndFunction("UpdateSession");
        }

        public OperationResult AbortScript(string usrSessionId, string scriptId)
        {
            BeginFunction("AbortScript");
            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);

            if (!userIdOpResult)
            {
                WriteLog(userIdOpResult.Message, MessageType.Error);
                return userIdOpResult;
            }

            AbortScript(scriptId);
            OperationResult result = new OperationResult(userIdOpResult, DM4Consts.Ok);

            EndFunction("AbortScript");
            return result;
        }

        private OperationResult AbortScript(string scriptId)
        {
            BeginFunction("AbortScript");

            OperationResult result = new OperationResult(DM4Consts.Ok);

            Dictionary<string, string> tasks = GetTaskFromRunScriptHeader(scriptId);
            if (tasks != null)
                foreach (var taskId in from task in tasks where task.Value == RshStatus.Run select task.Key)
                {
                    result = AbortTask(taskId);
                    if (!result) break;
                }

            EndFunction("AbortScript");
            return result;
        }

        public OperationResult AbortTask(string usrSessionId, string taskId)
        {
            BeginFunction("AbortTask");
            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);

            if (!userIdOpResult)
            {
                WriteLog(userIdOpResult.Message, MessageType.Error);
                return userIdOpResult;
            }

            AbortTask(taskId);
            OperationResult result = new OperationResult(userIdOpResult, DM4Consts.Ok);

            EndFunction("AbortTask");
            return result;
        }

        private OperationResult AbortTask(string taskId)
        {
            BeginFunction("AbortTask");

            OperationResult result = new OperationResult(DM4Consts.Ok);

            Dictionary<string, string> sessions = GetSessionFromTasks(taskId);
            if (sessions != null)
                foreach (var sessionId in from session in sessions where session.Value == TskStatus.Run select session.Key)
                {
                    result = AbortSession(sessionId);
                    if (!result) break;
                }

            EndFunction("AbortTask");
            return result;
        }

        public OperationResult AbortSession(string usrSessionId, string sessionId)
        {
            BeginFunction("AbortSession");
            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);

            if (!userIdOpResult)
            {
                WriteLog(userIdOpResult.Message, MessageType.Error);
                return userIdOpResult;
            }

            OperationResult result = AbortSession(sessionId);
            result.UsrSessionId = userIdOpResult.UsrSessionId;

            EndFunction("AbortSession");
            return result;
        }

        private OperationResult AbortSession(string sessionId)
        {
            BeginFunction("AbortSession");
            OperationResult result = new OperationResult(DM4Consts.Ok);

            try
            {
                if (_sessions.ContainsKey(sessionId))
                    _sessions[sessionId].Cts.Cancel();
                else
                    throw new Exception(String.Format("Session with id [{0}] not found", sessionId));
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, MessageType.Error);
                result.Result = DM4Consts.ErrorUnknown;
            }

            EndFunction("AbortSession");
            return result;
        }

        public OperationResult ResumeScript(string usrSessionId, string scriptId)
        {
            BeginFunction("ResumeScript");
            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);

            if (!userIdOpResult)
            {
                WriteLog(userIdOpResult.Message, MessageType.Error);
                return userIdOpResult;
            }

            OperationResult result = ResumeScript(scriptId);
            result.UsrSessionId = userIdOpResult.UsrSessionId;

            EndFunction("ResumeScript");
            return result;
        }

        private OperationResult ResumeScript(string scriptId)
        {
            BeginFunction("ResumeScript");

            OperationResult result = new OperationResult(DM4Consts.Ok);

            Dictionary<string, string> tasks = GetTaskFromRunScriptHeader(scriptId);
            if (tasks != null)
                foreach (var taskId in from task in tasks where task.Value == RshStatus.Wait select task.Key)
                {
                    result = ResumeTask(taskId);
                    if (!result) break;
                }

            EndFunction("ResumeScript");
            return result;
        }

        public OperationResult ResumeTask(string usrSessionId, string taskId)
        {
            BeginFunction("ResumeTask");
            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);

            if (!userIdOpResult)
            {
                WriteLog(userIdOpResult.Message, MessageType.Error);
                return userIdOpResult;
            }

            OperationResult result = ResumeTask(taskId);
            result.UsrSessionId = userIdOpResult.UsrSessionId;

            EndFunction("ResumeTask");
            return result;
        }

        private OperationResult ResumeTask(string taskId)
        {
            BeginFunction("ResumeTask");

            OperationResult result = new OperationResult(DM4Consts.Ok);

            Dictionary<string, string> sessions = GetSessionFromTasks(taskId);
            if (sessions != null)
                foreach (var sessionId in from session in sessions where session.Value == TskStatus.Wait select session.Key)
                {
                    result = ResumeSession(sessionId);
                    if (!result) break;
                }

            EndFunction("ResumeTask");
            return result;
        }

        public OperationResult ResumeSession(string usrSessionId, string sessionId)
        {
            BeginFunction("ResumeSession");
            OperationResult userIdOpResult = GetUserIdBySession(usrSessionId);

            if (!userIdOpResult)
            {
                WriteLog(userIdOpResult.Message, MessageType.Error);
                return userIdOpResult;
            }

            OperationResult result = ResumeSession(sessionId);
            result.UsrSessionId = userIdOpResult.UsrSessionId;

            EndFunction("ResumeSession");
            return result;
        }

        private OperationResult ResumeSession(string sessionId)
        {
            BeginFunction("ResumeSession");
            OperationResult result = new OperationResult(DM4Consts.Ok);

            try
            {
                if (_sessions.ContainsKey(sessionId))
                {
                    SessionEntry sessionResume = _sessions[sessionId];
                    long tskId, rshId;
                    if (GetRshIdAndTskIdFromSession(sessionId, out rshId, out  tskId))
                    {
                        UpdateSession(sessionId, CnSessions.Status, RshStatus.Run);
                        UpdateTasks(tskId, CnTask.Status, TskStatus.Run);
                        UpdateRunScriptHeader(rshId, CnRunScriptHeader.Status, DM4Consts.SessionRun);
                    }

                    //search session in database
                    string select = @"SELECT * FROM [sessions] WHERE UPPER([sess_id]) = @sess_id";

                    using (SqlCommand sc = new SqlCommand(select, _conn))
                    {
                        sc.Parameters.AddWithValue("@sess_id", sessionId);
                        using (SqlDataReader reader = sc.ExecuteReader())
                        {
                            if (reader.HasRows) //resume old sessions
                            {
                                while (reader.Read())
                                {
                                    Dictionary<string, ParamEntry> prm = new Dictionary<string, ParamEntry>();

                                    Int64 x = SysUtils.ConvertFromDBVal<Int64>(reader[CnSessions.StartPos]);
                                    Int64 y = SysUtils.ConvertFromDBVal<Int64>(reader[CnSessions.EndPos]);
                                    //string sessionId = SysUtils.ConvertFromDBVal<string>(reader["sess_id"]);
                                    string sessionStatus = SysUtils.ConvertFromDBVal<string>(reader[CnSessions.Status]);
                                    Int64 sessionTotalTime = SysUtils.ConvertFromDBVal<Int64>(reader[CnSessions.TotalTime]);
                                    Int64 sessionTraffic = SysUtils.ConvertFromDBVal<Int64>(reader[CnSessions.Traffic]);
                                    Int64 sessionRecords = SysUtils.ConvertFromDBVal<Int64>(reader[CnSessions.Records]);
                                    Int64 sessionErrors = SysUtils.ConvertFromDBVal<Int64>(reader[CnSessions.Errors]);
                                    int sessionUserId = SysUtils.ConvertFromDBVal<Int32>(reader[CnSessions.UsrId]);
                                    Dictionary<string, ParamEntry> sessionStartParams = StringUtils.StringToParams(SysUtils.ConvertFromDBVal<string>(reader[CnSessions.StartParams]));

                                    string modFile = GetModuleFileNameByCode(sessionResume.ModuleCode);

                                    StartSession(sessionResume.Task, modFile, sessionUserId, x, y, sessionStartParams, null, sessionId, true, sessionTotalTime, sessionTraffic, sessionRecords, sessionErrors);
                                }
                            }
                        }
                    }
                }
                else
                    throw new Exception(String.Format("Session with id [{0}] not found", sessionId));
            }
            catch (Exception ex)
            {
                WriteLog(ex.Message, MessageType.Error);
                result.Result = DM4Consts.ErrorUnknown;
            }

            EndFunction("ResumeSession");
            return result;
        }

        private string StartSession(string taskCode, string fileName, int usrId, Int64 start, Int64 end, Dictionary<string, ParamEntry> parameters, Dictionary<string, Dictionary<string, ParamEntry>> parentParams, string sessionId = "", bool needStart = true, Int64 totalWorkTime = 0, Int64 sessionTraffic = 0, Int64 sessionRecords = 0, Int64 sessionErrors = 0)
        {
            BeginFunction("StartSession");
            SysUtils.Sleeps.MSeconds(2); //pause for not very quick startup sessions

            //prepare session params
            string newSessionId = (sessionId == "") ? String.Format("{0}_{1}_{2}", taskCode, start, end) : sessionId;
            CancellationTokenSource cts = new CancellationTokenSource();

            DmTaskParams prm = new DmTaskParams
                                   {
                                       SessionParams = parameters,
                                       SessionId = newSessionId,
                                       Ct = cts.Token,
                                       UpdateSessionDelegate = UpdateSession,
                                       PrevWorkTime = totalWorkTime,
                                       PrevTraffic = sessionTraffic,
                                       UsrId = usrId,
                                       RecordCount = sessionRecords,
                                       ErrorCount = sessionErrors
                                   };

            if (fileName != null)
            {
                IModule moduleIntrface = (IModule)GetInterface(fileName, Interfaces.Module);
                if (moduleIntrface != null)
                {
                    prm.Module = moduleIntrface;

                    SessionEntry se = new SessionEntry
                    {
                        SessionId = newSessionId,
                        Task = taskCode,
                        Cts = cts,
                        Module = moduleIntrface,
                        ModuleCode = moduleIntrface.GetModuleCode()
                    };

                    prm.Ct = se.Cts.Token;
                    Action<object> action = StartOneTask;
                    System.Threading.Tasks.Task task = new System.Threading.Tasks.Task(action, prm, se.Cts.Token);
                    se.SessionTask = task;

                    if (_sessions.ContainsKey(newSessionId))
                        _sessions.Remove(newSessionId);

                    _sessions.Add(newSessionId, se);

                    //add to dataset if new session
                    if (sessionId == "")
                    {
                        string sql = String.Format("INSERT INTO sessions([sess_id], [sess_mod_code], [sess_task], [sess_start_params], [sess_startpos], [sess_endpos], [sess_usr_id])" +
                                                                 "VALUES('{0}', '{1}', '{2}', '{3}', {4}, {5}, {6})",
                            newSessionId, se.Module.GetModuleCode(), se.Task, StringUtils.ParamsToString(parameters), start, end, usrId);

                        WriteDebugLog(sql);

                        using (SqlCommand upd = new SqlCommand(sql, _conn))
                            upd.ExecuteNonQuery();

                    }

                    if (needStart)
                        task.Start();
                }
            }

            EndFunction("StartSession");
            return newSessionId;
        }

        #endregion
    }
}
